#ifndef PW_PBL_META_VHDATA_HPP
#define PW_PBL_META_VHDATA_HPP

#include "VLUInt.hpp"
#include "../IntFor.hpp"

namespace pw_pbl::Meta
{
	namespace _vhdata
	{
		template <typename T> requires(sizeof(T) >= alignof(T))
		using HeaderCell = UIntFor::Size<std::min(alignof(T), alignof(size_t))>;

		template <typename T, size_t... SIZE>
		using Header = std::conditional_t<std::is_const_v<T>, const VLUInt<HeaderCell<T>, size_t, SIZE...>, VLUInt<HeaderCell<T>, size_t, SIZE...>>;

		template <typename THeader, typename TData, bool... PACKED>
		static constexpr bool EPacked =
			(alignof(TData) < alignof(THeader) && (PACKED || ...)) || (alignof(TData) > alignof(THeader) && (PACKED && ...));

		template <size_t N>
		struct TPad
		{
			char _pad[N];
		};
		template <>
		struct TPad<0>
		{};

		template <typename VBASE, typename THeader, typename TData, bool... PACKED>
		struct DeGap : public TPad<(alignof(TData) > alignof(THeader) ? alignof(TData) - alignof(THeader) : 0)>, public VBASE
		{
			THeader header;
			TData data;

			template <typename... T>
			constexpr DeGap(T &&...v) noexcept : data(std::forward<T>(v)...) {}
		};

		template <typename VBASE, typename THeader, typename TData, bool... PACKED> requires EPacked<THeader, TData, PACKED...>
		struct [[gnu::packed]] alignas(std::min(alignof(THeader), alignof(TData))) DeGap<VBASE, THeader, TData, PACKED...> : public VBASE
		{
			THeader header;
			TData data;

			template <typename... T>
			constexpr DeGap(T &&...v) noexcept : data(std::forward<T>(v)...) {}
		};
	} // namespace _vhdata

	template <typename CELL, typename T = void, bool... PACKED> requires(sizeof...(PACKED) <= 1)
	struct VHData;

	template <typename CELL>
	struct VHData<CELL>
	{
		inline auto &header() noexcept { return *reinterpret_cast<_vhdata::Header<CELL> *>(this); }
		inline auto &header() const noexcept { return *reinterpret_cast<const _vhdata::Header<CELL> *>(this); }

		constexpr size_t dataSize() const noexcept { return header().value(); }
		constexpr size_t headerSize() const noexcept { return header().size(); }
		constexpr auto *data() noexcept { return header().end(); }
		constexpr auto *end() noexcept { return data() + dataSize(); }
		constexpr auto *data() const noexcept { return header().end(); }
		constexpr auto *end() const noexcept { return data() + dataSize(); }

		template <typename T>
		constexpr size_t write(T &&obj, size_t maxBytesSize) noexcept
		{
			auto sz = sizeof(T);
			if (sz >= maxBytesSize)
				return 0;
			auto nhdr = header().encode(sz);
			auto cellCnt = nhdr.size();
			if (cellCnt * nhdr.ECellSize > maxBytesSize - sz)
				return 0;
			header().assign(nhdr);
			new (header().data() + cellCnt) T{std::forward<T>(obj)};
			return sz + cellCnt * nhdr.ECellSize;
		}
	};

	template <typename CELL, typename T, bool... PACKED> requires(sizeof...(PACKED) <= 1)
	struct VHData : public _vhdata::DeGap<VHData<CELL>, _vhdata::Header<CELL, sizeof(T)>, T, PACKED...>
	{
		using base = _vhdata::DeGap<VHData<CELL>, _vhdata::Header<CELL, sizeof(T)>, T, PACKED...>;
		using value_type = T;
		static constexpr size_t ESize = sizeof(T);

		constexpr VHData(const value_type &_data) noexcept : base{_data} {}

		constexpr operator value_type &() noexcept { return this->data; }
		constexpr operator const value_type &() const noexcept { return this->data; }

		constexpr bool operator==(const VHData &) const = default;
		constexpr bool operator==(auto &) const { return false; }
	};

	template <typename T>
	VHData(const T &) -> VHData<uint8_t, T>;

	template <std::unsigned_integral CELL> requires(alignof(CELL) <= alignof(size_t))
	struct VHDataPtr
	{
		static constexpr auto EIsConst = std::is_const_v<CELL>;
		using TData = std::conditional_t<EIsConst, const VHData<std::remove_const_t<CELL>>, VHData<CELL>>;

		union
		{
			TData *pData;
			std::conditional_t<EIsConst, const void, void> *pVoid;
		};

		constexpr VHDataPtr(TData &raw) noexcept : pData{&raw} {}

		constexpr TData &operator*() const noexcept { return *pData; }
		constexpr TData *operator->() const noexcept { return pData; }
		explicit constexpr operator bool() const noexcept { return pData != nullptr; }

		/// 从任意指针构造vl指针，注意必须确认指针指向的布局为VHData（vl头+纯数组）
		template <typename T> requires(alignof(T) <= alignof(size_t))
		explicit constexpr VHDataPtr(T *p) noexcept : pVoid(p) {}
	};

	template <typename C>
	VHDataPtr(VHData<C> &) -> VHDataPtr<C>;

	template <typename C>
	VHDataPtr(const VHData<C> &) -> VHDataPtr<const C>;
} // namespace pw_pbl::Meta

#endif // PW_PBL_META_VHDATA_HPP
